import java.nio.ByteBuffer;

import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.ARBVertexBufferObject;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GLContext;


public class OpenGLStarter {
  private static final int FRAMERATE = 30;

  public void OpenGLStart() {
    start();
  }
  
  public void start() {
    boolean exitModule = false;
    
    try {  
      /**********************************************
        Create the display.
      **********************************************/
        DisplayMode mode = new DisplayMode(800, 600);
        
        Display.setDisplayMode(mode);  
        Display.setTitle("OpenGL Vertex Buffer Example");
        Display.setFullscreen(false);
        Display.setVSyncEnabled(false);
        Display.create();
      
      /**********************************************
        Initialize OpenGL but make sure that the graphics
        card is capable of using Vertex Buffer Objects.
      **********************************************/
        if (GLContext.getCapabilities().GL_ARB_vertex_buffer_object == false) {
          
          throw new RuntimeException("OpenGL Vertex Buffer Objects are not supported by Graphics Card. Unable to run program.");
          
        } else {
          
          /**********************************************
            Init OpenGL
          **********************************************/
            GL11.glEnable(GL11.GL_BLEND);
            GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);//_MINUS_SRC_ALPHA);
            
            GL11.glMatrixMode(GL11.GL_PROJECTION);
            GL11.glLoadIdentity();
            GL11.glFrustum(-1, 1, -1, 1, 1, 1000);
            
            GL11.glMatrixMode(GL11.GL_MODELVIEW);
            GL11.glLoadIdentity();
            
        }
		  
      /**********************************************
        In this simple example we will create a quad
        that is defined by four vertecies.
        
        First thing we have to do is create two vertex
        buffer objects(vbo). One will hold the quad's vertex
        attributes and the other will hold the index to
        each quad vertex.      
        
        Our first vbo will contain the quad's vertex 
        attributes which can describe things like the vertex 
        position in 3D space, normal, color, and texture
        coordinates. However, to keep things simple in this
        example we will only work with the vertex positions
        in 3D space. Our first vbo will contain this data.
        
        Quad vertex position in 3D space (x, y, z):
        
        (-1, 1, -3.0f)   ____________  (1, 1, -3.0f)
                       |              |
                       |              |
                       |              |
                       |              |
                       |              |
                       |              |
        (-1, -1, -3.0f)  ____________  (1, -1, -3.0f)
        
        
        
        
        The second vbo will hold the vertex id numbers, because
        each vertex within the quad is identified by an id. There 
        are four vertecies in a quad so we will need 4 ids
        inside our second vbo.
        
        Quad vertex index Id numbers in clock-wise direction:
        
        0 ____________ 1
        |              |
        |              |
        |              |
        |              |
        |              |
        |              |
        3 ____________ 2
        
        
        So let's begin.        
      **********************************************/
      
      
      /**********************************************
        Create the vbo
      **********************************************/
  		  int vboVertexAttributes = ARBVertexBufferObject.glGenBuffersARB();
  		  int vboVertexIndecies = ARBVertexBufferObject.glGenBuffersARB();
		  
  		/**********************************************
        Since the vbo is a array that holds all our
        attributes we need to tell opengl how big
        each section of attributes is.

        To describe one of the vertex positions in 
        the quad we have to provide 3 axis locations,
        x, y, and z. To describe those positions we
        will use floats. 
        
        
        Quad vertex position in 3D space (x, y, z):
        
        (-1, 1, -3.0f)   ____________  (1, 1, -3.0f)
                       |              |
                       |              |
                       |              |
                       |              |
                       |              |
                       |              |
        (-1, -1, -3.0f)  ____________  (1, -1, -3.0f)
        
        
        
        vertex(0): (-1.0, 1.0, 0.0) = 3 floats
        
        
        In java a float is 4 bytes long. So 3 floats
        that are each 4 bytes long equals 12 bytes 
        total that we need to define the position of
        each vertex. In other words:
        
        3 floats x 4 bytes = 12 bytes
        
        each vertex position is 12 bytes long. We will
        create a variable call 'vertexPositionAttributeSize' that
        holds this value.
        
        Since the quad we are defining needs 4 vertex
        positions then we can assume:
        
        12 bytes x 4 vertex positions = 48 bytes
        
        Our vbo that contains the vertex positions will
        be a total of 48 bytes long.
        
        Same thing goes for the vertex index size.
        We will use an integer to describe each id 
        for each vertex on the quad. Integers in java
        are 4 bytes long. Since we only need one integer
        to describe one vertex index our vertex index
        vbo would be described as:
        
        1 integer = 4 bytes
        1 vertex id = 1 integer
        
        1 quad = 4 vertex Id's
        
        vertex index vbo size = (4 vertex Id's) x (4 bytes) = 16 bytes
        
        vertex index vbo size = 16 bytes
  
      **********************************************/
        int totalNumberOfAxis = 3;
        int floatSize = 4;
  		  int vertexPositionAttributeSize = (totalNumberOfAxis * floatSize);
  		  int vertexIndexSize = 4;
  		  int totalVertecies = 4;
  		  
		  /**********************************************
        In OpenGL you can have a bunch of vbo's but 
        in order to tell OpenGL which one you want
        to work with you have to set the current 
        vbo using glBindBuffer(). In lwjgl the command
        is glBindBufferARB().
        
        Since we want to work with the Vertex Attributes
        vbo we need to set that as the current vbo.
      **********************************************/
        ARBVertexBufferObject.glBindBufferARB(
          ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB,
          vboVertexAttributes
        );
        
      /**********************************************
        Now we need to set the size of the Vertex
        Attributes vbo.
      **********************************************/
        ARBVertexBufferObject.glBufferDataARB(
          ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB,
          (vertexPositionAttributeSize * totalVertecies),
          ARBVertexBufferObject.GL_STATIC_DRAW_ARB
        );
        
      /**********************************************
        Then we use a java io ByteBuffer to store the
        actual vertex position attributes. The ByteBuffer
        will be returned to us by the glMapBufferARB()
        call.
      **********************************************/
        ByteBuffer vertexPositionAttributes = ARBVertexBufferObject.glMapBufferARB(
          ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, ARBVertexBufferObject.GL_WRITE_ONLY_ARB,
          (vertexPositionAttributeSize * totalVertecies),
          null
        );
        
      /**********************************************
        Now define the vertex positions by filling
        in the ByteBuffer array.
      **********************************************/
        // vertex index 0
        vertexPositionAttributes.putFloat(-1.0f);
        vertexPositionAttributes.putFloat(1.0f);
        vertexPositionAttributes.putFloat(-3.0f);
        
        // vertex index 1
        vertexPositionAttributes.putFloat(1.0f);
        vertexPositionAttributes.putFloat(1.0f);
        vertexPositionAttributes.putFloat(-3.0f);
        
        // vertex index 2
        vertexPositionAttributes.putFloat(1.0f);
        vertexPositionAttributes.putFloat(-1.0f);
        vertexPositionAttributes.putFloat(-3.0f);
        
        // vertex index 3
        vertexPositionAttributes.putFloat(-1.0f);
        vertexPositionAttributes.putFloat(-1.0f);
        vertexPositionAttributes.putFloat(-3.0f);

		  /**********************************************
        Since we are done defining the quad we need
        to switch the ByteBuffer mode to allow for java
        read operations.
      **********************************************/
        vertexPositionAttributes.flip();
      
      /**********************************************
        Now we need to tell OpenGL stop using the vertex
        attribute vbo. Essentially we are done writing
        to it.
        
        We have to unmap the buffer and then un-bind
        the vbo buffer.
      **********************************************/
        ARBVertexBufferObject.glUnmapBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB);
        ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, 0);
        
      /**********************************************
        So essentially to write to a vbo there is a 
        process that follows:
        
        1) Tell OpenGL which vbo to work with. This is
        done by binding the vbo as the current vbo.
        
        2) Do your operations on that vbo.
        
        3) Unset the vbo or unbind it.
        
        4) Go back to set 1 to work with another vbo. Or
        move on to your next thing to do.        
      **********************************************/
      
      /**********************************************
        Now we need to determine the size of vertex
        index vbo.
        
        Quad vertex index Id numbers in clock-wise direction:
        
        0 ____________ 1
        |              |
        |              |
        |              |
        |              |
        |              |
        |              |
        3 ____________ 2
        
        We will use an integer to describe each id 
        for each vertex on the quad. Integers in java
        are 4 bytes long. Since we only need one integer
        to describe one vertex index our vertex index
        vbo would be described as:
        
        1 integer = 4 bytes
        1 vertex id = 1 integer
        
        1 quad = 4 vertex Id's
        
        vertex index vbo size = (4 vertex Id's) x (4 bytes) = 16 bytes
        
        vertex index vbo size = 16 bytes
      **********************************************/
  		  vertexIndexSize = 4;
  		  
      /**********************************************
        So since we need to also set the data for the
        vertex index vbo, the process is the same as
        for setting the vertex positions above. Only 
        this time we are only describing the index
        id for each vertex in the quad. Which are 0, 1, 2, and 3.
      **********************************************/
      
      /**********************************************
        Set(Bind) the current vbo that we need to work
        with
      **********************************************/
        ARBVertexBufferObject.glBindBufferARB(
          ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 
          vboVertexIndecies
        );
        
        ARBVertexBufferObject.glBufferDataARB(
          ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 
          (vertexIndexSize * totalVertecies), 
          ARBVertexBufferObject.GL_STATIC_DRAW_ARB
        );
        
      /**********************************************
        Get the ByteBuffer for the vertex index vbo
      **********************************************/
        ByteBuffer vertexIndecies = ARBVertexBufferObject.glMapBufferARB(
          ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 
          ARBVertexBufferObject.GL_WRITE_ONLY_ARB, 
          (vertexIndexSize * totalVertecies), 
          null
        );
       
      /**********************************************
        Write the vertex index Id's to the ByteBuffer
      **********************************************/
        vertexIndecies.putInt(0);
        vertexIndecies.putInt(1);
        vertexIndecies.putInt(2);
        vertexIndecies.putInt(3);
        
      /**********************************************
        Set the ByteBuffer for read operations
      **********************************************/
        vertexIndecies.flip();
      
      /**********************************************
        Unset/UnBind the vertex index vbo
      **********************************************/
        ARBVertexBufferObject.glUnmapBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB);
        ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
      
      
      while (exitModule == false) {
        Display.update();
        
        if ((Display.isCloseRequested() == true) || (Keyboard.isKeyDown(Keyboard.KEY_ESCAPE) == true)) {
          exitModule = true;
        } else if (Display.isActive() == true) {
          
          /**********************************************
            Clear the drawing buffer
          **********************************************/
            GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
          
          /**********************************************
            Enable the vertex buffer objects/arrays for use
          **********************************************/
            GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
          
          /**********************************************
            Set which vbo's to use. We will set our vertex
            attributes vbo as the vbo with the vertex data.
            Then we will set the vertex index vbo as the 
            vbo with the vertex indexes.
          **********************************************/
            ARBVertexBufferObject.glBindBufferARB(
              ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB,
              vboVertexAttributes
            );
            
            ARBVertexBufferObject.glBindBufferARB(
              ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB,
              vboVertexIndecies
            );

          /**********************************************
            Call the OpenGL command to get the vertex position
            and then use the vertex indexes to draw a quad
            based on those positions. 
            
            This drawing process can get a bit more involved
            once you start adding normals, vertex colors, and
            texture coords. This example is just to show
            how to get a quad on screen. From there you can
            check out other online tutorials which explain how
            to do more things, like adding color to each vertex.
            
          **********************************************/
            // Get the vertex position data
            GL11.glVertexPointer(
              totalNumberOfAxis,
              GL11.GL_FLOAT, 
              vertexPositionAttributeSize, 
              0
            );
            
            // Draw the quad using the vertex indexes
            GL11.glDrawElements(
              GL11.GL_QUADS, 
              totalVertecies, 
              GL11.GL_UNSIGNED_INT, 
              0
            );
            
          /**********************************************
            Unset/UnBind the quads vertex buffer objects
          **********************************************/
            ARBVertexBufferObject.glBindBufferARB(
              ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB,
              0
            );
            
            ARBVertexBufferObject.glBindBufferARB(
              ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB,
              0
            );
          
          /**********************************************
            Disable the vertex buffer objects/arrays
          **********************************************/
            GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY);
        }
      }
      
      /**********************************************
        Exit the program
      **********************************************/
        Display.destroy();
        System.exit(0);
    
    } catch (Exception e) {
    }
  }
  
  public static void main(String[] args){
	  new OpenGLStarter();
  }
  
}